package com.galaxy.device.config;

import com.alibaba.fastjson2.JSON;
import com.galaxy.device.domain.vo.DeviceBaseVo;
import com.galaxy.device.domain.vo.DeviceLocationVo;
import com.galaxy.device.utils.EsWrappersUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Elasticsearch初始化类
 */
@Slf4j
@Component
@Configuration
public class ElasticsearchInitializer implements CommandLineRunner {

    private static final String INDEX_NAME = "device";
    private static final int MAX_RETRY = 3;
    private static final long RETRY_DELAY = 5000; // 5秒
    private static final int CONNECTION_TIMEOUT = 5000; // 5秒
    private static final int SOCKET_TIMEOUT = 10000; // 10秒

    @Value("${easy-es.index-prefix:galaxy_}")
    private String indexPrefix;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private EsWrappersUtils esWrappersUtils;

    /**
     * 禁用Elasticsearch健康检查
     */
    @Bean
    public HealthIndicator elasticsearchHealthIndicator() {
        return () -> org.springframework.boot.actuate.health.Health.up().build();
    }

    /**
     * 获取完整的索引名称
     */
    private String getFullIndexName() {
        return indexPrefix + INDEX_NAME;
    }

    @Override
    public void run(String... args) {
        int retryCount = 0;
        boolean success = false;

        while (retryCount < MAX_RETRY && !success) {
        try {
                // 检查连接
                if (!checkConnection()) {
                    log.warn("Elasticsearch连接失败，等待重试...");
                    TimeUnit.MILLISECONDS.sleep(RETRY_DELAY);
                    retryCount++;
                    continue;
                }

                createIndexIfNotExists();
                success = true;
                log.info("Elasticsearch索引初始化成功");
            } catch (Exception e) {
                retryCount++;
                log.error("初始化 Elasticsearch 索引失败 (尝试 {}/{}): {}", retryCount, MAX_RETRY, e.getMessage());
                if (retryCount < MAX_RETRY) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(RETRY_DELAY);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        if (!success) {
            log.error("Elasticsearch索引初始化失败，已达到最大重试次数");
        }
    }

    /**
     * 检查Elasticsearch连接
     */
    private boolean checkConnection() {
        try {
            // 尝试获取集群信息
            GetIndexResponse response = restHighLevelClient.indices().get(new GetIndexRequest("_all"), RequestOptions.DEFAULT);
            log.info("成功连接到Elasticsearch集群，当前索引数: {}", response.getAliases().size());
            return true;
        } catch (Exception e) {
            log.error("Elasticsearch连接检查失败: {}", e.getMessage());
            return false;
        }
    }

    private void createIndexIfNotExists() throws Exception {
        String fullIndexName = getFullIndexName();
        GetIndexRequest getIndexRequest = new GetIndexRequest(fullIndexName);
        boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);

        if (!exists) {
            CreateIndexRequest request = new CreateIndexRequest(fullIndexName);

            // 设置索引配置
            request.settings(Settings.builder()
                .put("index.number_of_shards", 1)
                .put("index.number_of_replicas", 1)
                .put("index.refresh_interval", "1s")
                .put("index.max_result_window", 10000)
                .put("index.requests.cache.enable", true)
            );

            // 设置映射
            Map<String, Object> properties = new HashMap<>();
            properties.put("id", Map.of("type", "keyword"));
            properties.put("deviceId", Map.of("type", "keyword"));
            properties.put("deviceName", Map.of(
                "type", "text",
                "analyzer", "standard",
                "search_analyzer", "standard"
            ));
            properties.put("siteName", Map.of(
                "type", "text",
                "analyzer", "standard",
                "search_analyzer", "standard"
            ));
            properties.put("location", Map.of(
                "type", "geo_point"
            ));
            properties.put("address", Map.of(
                "type", "text",
                "analyzer", "standard",
                "search_analyzer", "standard"
            ));
            properties.put("latitude", Map.of("type", "double"));
            properties.put("longitude", Map.of("type", "double"));
            properties.put("createTime", Map.of(
                "type", "date",
                "format", "yyyy-MM-dd HH:mm:ss||epoch_millis"
            ));
            properties.put("updateTime", Map.of(
                "type", "date",
                "format", "yyyy-MM-dd HH:mm:ss||epoch_millis"
            ));

            Map<String, Object> mapping = new HashMap<>();
            mapping.put("properties", properties);
            request.mapping(mapping);

            boolean acknowledged = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT).isAcknowledged();
            if (acknowledged) {
                log.info("成功创建索引: {}", fullIndexName);
            } else {
                log.error("创建索引失败: {}", fullIndexName);
                throw new RuntimeException("创建索引失败");
            }
        } else {
            log.info("索引 {} 已存在", fullIndexName);
        }
    }

    public boolean saveDevice(DeviceBaseVo device) {
        try {
            if (!checkConnection()) {
                log.error("Elasticsearch连接不可用，无法保存设备信息");
                return false;
            }

            String id = String.valueOf(device.getId());
            Map<String, Object> data = JSON.parseObject(JSON.toJSONString(device), Map.class);

            // 使用工具类更新文档
            boolean success = esWrappersUtils.updateById(getFullIndexName(), id, data);
            if (!success) {
                // 如果更新失败，尝试插入
                IndexRequest indexRequest = new IndexRequest(getFullIndexName())
                    .id(id)
                    .source(data, XContentType.JSON);
                restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            }
            return true;
        } catch (Exception e) {
            log.error("保存设备信息到 Elasticsearch 失败: {}", e.getMessage());
            return false;
        }
    }

    public boolean saveOrUpdateDeviceLocation(DeviceLocationVo locationVo) {
        try {
            if (!checkConnection()) {
                log.error("Elasticsearch连接不可用，无法保存设备位置信息");
                return false;
            }

            String id = String.valueOf(locationVo.getId());
            Map<String, Object> data = new HashMap<>();
            data.put("id", locationVo.getId());
            data.put("deviceId", locationVo.getDeviceId());
            data.put("location", new GeoPoint(Double.parseDouble(String.valueOf(locationVo.getLatitude())), Double.parseDouble(String.valueOf(locationVo.getLocation()))));
            data.put("address", locationVo.getAddress());
            data.put("latitude", locationVo.getLatitude());
            data.put("longitude", locationVo.getLongitude());
            data.put("updateTime", System.currentTimeMillis());

            // 使用工具类更新文档
            boolean success = esWrappersUtils.updateById(getFullIndexName(), id, data);
            if (!success) {
                // 如果更新失败，尝试插入
                IndexRequest indexRequest = new IndexRequest(getFullIndexName())
                    .id(id)
                    .source(data, XContentType.JSON);
                restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            }
            return true;
        } catch (Exception e) {
            log.error("保存设备位置信息到 Elasticsearch 失败: {}", e.getMessage());
            return false;
        }
    }

    public List<DeviceLocationVo> listNearbyDevices(Double longitude, Double latitude, Double distance) {
        try {
            if (!checkConnection()) {
                log.error("Elasticsearch连接不可用，无法查询附近设备");
                return new ArrayList<>();
            }

            // 构建地理位置查询
            GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location")
                .point(latitude, longitude)
                .distance(distance, DistanceUnit.KILOMETERS);

            // 使用工具类进行查询
            List<Map<String, Object>> resultList = esWrappersUtils.list(getFullIndexName(), geoQuery);

            List<DeviceLocationVo> devices = new ArrayList<>();
            for (Map<String, Object> data : resultList) {
                DeviceLocationVo device = JSON.parseObject(JSON.toJSONString(data), DeviceLocationVo.class);
                devices.add(device);
            }
            return devices;
        } catch (Exception e) {
            log.error("查询附近设备失败", e);
            return new ArrayList<>();
        }
    }

    public List<DeviceLocationVo> pageNearbyDevices(Double longitude, Double latitude, Double distance, Integer pageNum, Integer pageSize) {
        try {
            if (!checkConnection()) {
                log.error("Elasticsearch连接不可用，无法分页查询附近设备");
                return new ArrayList<>();
            }

            // 构建地理位置查询
            GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location")
                .point(latitude, longitude)
                .distance(distance, DistanceUnit.KILOMETERS);

            // 使用工具类进行分页查询
            List<Map<String, Object>> resultList = esWrappersUtils.page(getFullIndexName(), geoQuery, pageNum, pageSize);

            List<DeviceLocationVo> devices = new ArrayList<>();
            for (Map<String, Object> data : resultList) {
                DeviceLocationVo device = JSON.parseObject(JSON.toJSONString(data), DeviceLocationVo.class);
                devices.add(device);
            }
            return devices;
        } catch (Exception e) {
            log.error("分页查询附近设备失败", e);
            return new ArrayList<>();
        }
    }
}
